Godot 4 信号(Signal)的使用入门

J.sky
Signal
Godot教程
2025/12/30

信号(Signal)是Godot引擎中最强大、最重要的机制之一,它实现了观察者模式,让节点之间可以松耦合地通信。本教程将通过实际案例,带你全面掌握Godot中的信号使用方法。

什么是信号?

信号是一种事件通知机制。当一个节点发生某些事情时(比如按钮被点击、分数改变、动画播放完成等),它可以发出一个信号,其他节点可以"监听"这个信号,并在信号发出时执行相应的代码。

信号的核心优势:

  • 解耦:节点之间不需要直接引用,降低代码耦合度
  • 灵活:一个信号可以被多个节点监听
  • 清晰:代码结构更清晰,易于维护

一、内置信号的使用

Godot的许多节点都内置了信号,比如Button节点就有pressed信号。

1.1 场景结构

我们的main.tscn场景包含:

  • 一个Button节点(按钮)
  • 一个Label节点(标签),绑定label.gd脚本

signal

1.2 在编辑器中连接信号

signal

选中button后,在节点面板中设置信号

这表示:当Buttonpressed信号发出时,调用Label节点的_on_button_pressed方法。

1.3 编写响应函数

label.gd中,我们定义了响应函数:

extends Label

func _on_button_pressed() -> void:
    # 这里就是按钮点后会执行代码的地方
    self.text = "按钮被点击啦!!!!!"

运行效果:

  • 点击按钮
  • Button发出pressed信号
  • Label_on_button_pressed函数被调用
  • 标签文本更新为"按钮被点击啦!!!!!"

signal

二、自定义信号

除了内置信号,我们还可以创建自己的信号,用于传递自定义事件。

2.1 声明自定义信号

score_manager.gd中,我们声明了一个带参数的信号:

extends Node

# 声明一个带参数的自定义信号,参数类型是整数 (int)
signal score_updated(new_score: int)

var current_score = 0

2.2 发出信号

当分数改变时,我们发出信号并传递新的分数值:

func increase_score(points: int):
    current_score += points
    # 当分数改变时,发出信号,并附带新的分数值
    emit_signal("score_updated", current_score)

这里使用了emit_signal()函数,第一个参数是信号名称,后面是传递给信号的参数。

注意:在Godot 4中,你也可以直接使用信号名称来发出信号:

score_updated.emit(current_score)

2.3 连接自定义信号

score_label.gd中,我们连接到这个信号:

extends Label
@onready var score_manager: Node2D = $".." #连接到场景中的根节点ScoreManager

func _ready():
    # 在 _ready 函数中连接信号
    # 当 score_manager 发出 score_updated 信号时,执行 _on_score_manager_score_updated
    score_manager.score_updated.connect(_on_score_manager_score_updated)

# 这是我们响应信号的函数
func _on_score_manager_score_updated(new_score: int):
    # 更新 Label 的文本,显示新的分数
    self.text = "Score: " + str(new_score)

关键点解析:

  • @onready:延迟初始化,确保节点树已经构建完成
  • connect():连接信号的方法
  • 响应函数的参数必须与信号声明的参数一致

2.4 测试信号

score_manager.gd_ready函数中,我们测试信号:

func _ready() -> void:
    increase_score(10) # ScoreLabel 显示 Score: 10
    await get_tree().create_timer(2.0).timeout  # 等待2秒
    increase_score(5)  # ScoreLabel 显示 Score: 15
    await get_tree().create_timer(2.0).timeout  # 等待2秒
    increase_score(5)  # ScoreLabel 显示 Score: 20

运行效果:

  • 场景加载后,分数立即变为10
  • 等待2秒后,分数变为15
  • 再等待2秒后,分数变为20

三、信号的两种连接方式

3.1 在编辑器中连接(可视化方式)

步骤:

  1. 选择要发出信号的节点(如Button)
  2. 在右侧"节点"面板中,找到"信号"选项卡
  3. 双击要连接的信号(如pressed)
  4. 选择接收信号的节点和方法
  5. Godot会自动生成响应函数的框架代码

优点:

  • 可视化操作,直观易懂
  • 不需要写连接代码
  • 适合简单的信号连接

3.2 在代码中连接(编程方式)

score_manager.score_updated.connect(_on_score_manager_score_updated)

优点:

  • 更灵活,可以在运行时动态连接
  • 可以传递额外的参数
  • 适合复杂的信号连接

四、信号的高级用法

4.1 断开信号连接

如果不再需要监听某个信号,可以断开连接:

score_manager.score_updated.disconnect(_on_score_manager_score_updated)

4.2 一次性信号

使用call_deferred可以让信号只触发一次:

button.pressed.connect(_on_button_pressed, CONNECT_ONE_SHOT)

4.3 信号带多个参数

信号可以传递多个参数:

signal player_died(player_name: String, score: int, cause: String)

emit_signal("player_died", "Player1", 100, "Enemy")

4.4 检查信号是否已连接

if score_manager.score_updated.is_connected(_on_score_manager_score_updated):
    print("信号已连接")

五、最佳实践

5.1 命名规范

  • 信号名称使用动词形式:score_updatedplayer_diedgame_over
  • 响应函数命名:_on_节点名_信号名,如_on_button_pressed

5.2 什么时候使用信号?

适合使用信号的场景:

  • UI交互(按钮点击、输入框变化)
  • 游戏状态变化(分数更新、生命值改变)
  • 动画事件(动画播放完成、帧事件)
  • 碰撞检测(进入/退出碰撞区域)

不适合使用信号的场景:

  • 需要返回值的操作
  • 高频调用的每帧逻辑(使用_process_physics_process

5.3 性能考虑

信号连接本身开销很小,但要注意:

  • 避免在_process中频繁连接/断开信号
  • 大量信号监听者可能影响性能

六、总结

通过本教程,我们学习了:

  1. 内置信号:使用Godot预定义的信号(如Button的pressed)
  2. 自定义信号:创建和发出自己的信号
  3. 信号连接:在编辑器和代码中连接信号
  4. 信号参数:传递数据给信号监听者
  5. 高级用法:断开连接、一次性信号、多参数信号

信号是Godot开发中的核心概念,掌握它将让你的代码更加模块化、可维护。


项目文件说明

本教程项目包含以下文件:

  • main.tscn:演示内置信号(Button pressed)
  • label.gd:响应按钮点击信号
  • score_manager.tscn:演示自定义信号
  • score_manager.gd:定义和发出自定义信号
  • score_label.gd:监听并响应自定义信号

祝你在Godot开发之路上越走越远! 🚀

本文为原创文章,遵循: CC BY-NC-SA 4.0版权协议,转载请附上原文出处链接和本声明。

英雄请留步!欢迎在下方留言交流!